/**************************************************************************//**
 *   @file   command.c
 *   @brief  Implementation of AD9361 Command Driver.
 *   @author DBogdan (dragos.bogdan@analog.com)
 *******************************************************************************
 * Copyright 2013(c) Analog Devices, Inc.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  - Neither the name of Analog Devices, Inc. nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *  - The use of this software may or may not infringe the patent rights
 *    of one or more patent holders.  This license does not release you
 *    from the requirement that you obtain separate licenses from these
 *    patent holders to use this software.
 *  - Use of the software either in source or binary form, must be run
 *    on or directly connected to an Analog Devices Inc. component.
 *
 * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include "parameters.h"
#include "command.h"
#include "console.h"
#include "ad9361_api.h"
#include "ad9361.h"
#include "axi_lite.h"
#include "platform.h"

#include <math.h>

static double rx1_ext_gain_d = 0;
static double rx1_ext_attn_d = 0;
static double tx1_ext_gain_d = 0;
static double tx1_ext_attn_d = 0;

uint32_t control_PPS(double *last_val, uint8_t *flag, double clock_offset_ns){
    // last_val锟窖碉拷锟斤拷锟斤拷锟斤拷锟斤拷位为ns
    // flag每锟轿凤拷锟斤拷指锟筋，锟斤拷锟斤拷要锟斤拷转锟斤拷锟斤拷要锟斤拷锟斤拷锟叫讹拷指锟斤拷锟角凤拷锟截革拷锟斤拷锟斤拷
    // clock_offset_ns 锟斤拷锟节碉拷锟接差，锟斤拷位为 ns
    const double sample_ns = 1000.0/40;
    // 1 ns锟斤拷要锟侥诧拷锟斤拷锟斤拷锟斤拷
    const double steps_ns = 56;
    // 锟斤拷锟斤拷锟斤拷
    uint32_t control_word = 0;

    // 锟斤拷锟斤拷锟斤拷锟斤拷
    double offset_need = clock_offset_ns - (*last_val);
    int integer_cycle = 0;
    int VCO_phase = 0;
    // [31:13]          [12]        [11:0]
    // 锟斤拷锟斤拷ADC时锟接革拷锟斤拷   flag       小锟斤拷一锟斤拷时锟斤拷ADC锟斤拷锟斤拷
    // 锟斤拷锟斤拷时锟接匡拷锟斤拷一锟斤拷锟睫凤拷锟斤拷锟斤拷锟斤拷位锟斤拷锟斤拷要锟斤拷蔚锟斤拷锟斤拷锟斤拷锟斤拷20M锟斤拷锟斤拷锟绞ｏ拷20_000_000锟斤拷锟斤拷要25bit锟脚癸拷锟斤拷锟斤拷锟斤拷锟斤拷锟�19bit锟斤拷
    // 锟斤拷通锟斤拷只锟斤拷要锟节筹拷始锟阶讹拷锟斤拷要锟斤拷牡锟斤拷锟�
    // 锟斤拷锟斤拷小锟斤拷锟斤拷锟街ｏ拷每锟轿诧拷锟斤拷锟斤拷为 1/56 ns锟斤拷锟斤拷锟揭拷锟斤拷锟�1锟斤拷ADC锟斤拷锟斤拷锟斤拷锟斤拷要锟斤拷锟斤拷 50ns / (1/56) = 2800锟轿ｏ拷锟斤拷要12bit锟斤拷示
    integer_cycle = floor(offset_need/sample_ns);
    if(integer_cycle!=0){
        // 锟斤拷要锟斤拷锟斤拷锟斤拷一锟斤拷锟斤拷锟斤拷锟斤拷
        // 锟叫讹拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷歉锟斤拷锟斤拷锟斤拷
        if(integer_cycle>0){
            if(integer_cycle>262143){
                // 锟斤拷锟斤拷锟斤拷锟斤拷 2^18-1锟斤拷
                integer_cycle = 262143;
            }
            control_word = (integer_cycle<<13);
            // 锟斤拷锟斤拷剩锟斤拷锟斤拷锟斤拷锟斤拷锟�
        }
        else{
            if(integer_cycle<-262144){
                // 锟斤拷锟斤拷锟斤拷锟斤拷 -2^18锟斤拷
                integer_cycle = -262144;
            }
            control_word = (integer_cycle<<13);
        }
    }
    // 剩锟斤拷锟斤拷锟斤拷锟斤拷锟�
    double frac = offset_need - integer_cycle*sample_ns;
    if(frac>0 && frac<sample_ns){
        // 锟斤拷锟斤拷锟斤拷锟�1锟斤拷锟斤拷锟斤拷锟斤拷锟节ｏ拷锟斤拷锟轿诧拷锟斤拷锟斤拷小锟斤拷锟斤拷锟街ｏ拷小锟斤拷1锟斤拷锟斤拷锟斤拷锟斤拷锟节ｏ拷锟斤拷锟叫碉拷锟斤拷
        VCO_phase =  round(frac*steps_ns);
        control_word += VCO_phase;
    }

    // 写锟斤拷flag锟斤拷锟斤拷转
    control_word += ((*flag)<<12);
    *flag = !(*flag);
    // 锟斤拷锟姐本锟斤拷锟窖撅拷锟斤拷锟斤拷锟斤拷
    double offset_finish = integer_cycle*sample_ns + VCO_phase*1.0/steps_ns;
    *last_val = *last_val + offset_finish;
    return control_word;
}

void rx1_ext_gain(struct spi_device spi,uint32_t data)
{
	ad9361_spi_writef(&spi, REG_EXTERNAL_LNA_CTRL, BIT(4), 1);//(see UG671-pg15)
	ad9361_spi_writef(&spi, REG_GPO_FORCE_AND_INIT, BIT(4), data);//(see UG671-pg15)

}

void tx1_ext_gain(struct spi_device spi,uint32_t data)
{
	ad9361_spi_writef(&spi, REG_EXTERNAL_LNA_CTRL, BIT(4), 1);//
	ad9361_spi_writef(&spi, REG_GPO_FORCE_AND_INIT, BIT(6), data);//

}

void rx1_ext_attn(struct spi_device spi,uint32_t data)
{
	uint32_t temp = ~data;
	AXI_REG_WRITE(rx1_atten, temp);

}

//by z
void rx1_b_ext_attn(struct spi_device spi,uint32_t data)
{
	uint32_t temp = ~data;
	AXI_REG_WRITE(rx1_b_atten, temp);

}

void tx1_ext_attn(struct spi_device spi,uint32_t data)
{
	uint32_t temp = ~data;
	AXI_REG_WRITE(tx1_atten, temp);

}


//by z
void tx1_b_ext_attn(struct spi_device spi,uint32_t data)
{
	uint32_t temp = data;
	AXI_REG_WRITE(tx1_b_atten, temp);

}

/******************************************************************************/
/************************ Constants Definitions *******************************/
/******************************************************************************/
command cmd_list[] = {
	{"help?", "Displays all available commands.", "", get_help},
//	{"tx1_attenuation=", "Sets the TX1 attenuation [mdB].", "", set_tx1_attenuation},
	{"set_tx1_ext_gain=", "Sets tx1 exit gain(0=37.5dB,1=17.5dB).", "", set_tx1_ext_gain},
	{"set_tx1_ext_attn=", "Sets tx1 exit attn(N*0.5dB).", "", set_tx1_ext_attn},
//	{"rx1_rf_gain=", "Sets the RX1 RF gain.", "", set_rx1_rf_gain},
	{"set_rx1_ext_gain=", "Sets rx1 exit gain(0=37.5dB,1=17.5dB).", "", set_rx1_ext_gain},
	{"set_rx1_ext_attn=", "Sets rx1 exit attn(N*0.5dB).", "", set_rx1_ext_attn},

	{"get_tx1_ext_gain=", "Sets tx1 exit gain(0=37.5dB,1=17.5dB).", "", get_tx1_ext_gain},
	{"get_tx1_ext_attn=", "Sets tx1 exit attn(N*0.5dB).", "", get_tx1_ext_attn},
//	{"rx1_rf_gain=", "Sets the RX1 RF gain.", "", set_rx1_rf_gain},
	{"get_rx1_ext_gain=", "Sets rx1 exit gain(0=37.5dB,1=17.5dB).", "", get_rx1_ext_gain},
	{"get_rx1_ext_attn=", "Sets rx1 exit attn(N*0.5dB).", "", get_rx1_ext_attn},
	{"bin_update_cmd?", "brief@ bin_update", "example@ bin_update_cmd?", bin_update_cmd},
	{"get_FirmWare_Version?", "get_FirmWare_Version?", "", get_FirmWare_Version},
	{"set_iic_device=", "set_iic_device", "", set_iic_device},
	{"get_9361_temp?", "Get_9361_temp.", "", get_9361_temp},
	{"get_zynq_temp?", "Get_zynq_temp.", "", get_zynq_temp},

//add 9361_b command by z
	{"set_tx1_b_ext_gain=", "Sets tx1_b exit gain(0=37.5dB,1=17.5dB).", "", set_tx1_b_ext_gain},
	{"set_tx1_b_ext_attn=", "Sets tx1_b exit attn(N*0.5dB).", "", set_tx1_b_ext_attn},
	{"set_rx1_b_ext_gain=", "Sets rx1_b exit gain(0=37.5dB,1=17.5dB).", "", set_rx1_b_ext_gain},
	{"set_rx1_b_ext_attn=", "Sets rx1_b exit attn(N*0.5dB).", "", set_rx1_b_ext_attn},


};
const char cmd_no = (sizeof(cmd_list) / sizeof(command));

/******************************************************************************/
/************************ Variables Definitions *******************************/
/******************************************************************************/
extern struct ad9361_rf_phy *ad9361_phy;
/**************************************************************************//***
 * @brief Show the invalid parameter message.
 *
 * @return None.
*******************************************************************************/
void show_invalid_param_message(unsigned char cmd_no)
{
	console_print("Invalid parameter!\n");
	console_print("%s  - %s\n", (char*)cmd_list[cmd_no].name, (char*)cmd_list[cmd_no].description);
	console_print("Example: %s\n", (char*)cmd_list[cmd_no].example);
}

/**************************************************************************//***
 * @brief Displays all available commands.
 *
 * @return None.
*******************************************************************************/
void get_help(double* param, char param_no) // "help?" command
{
	unsigned char display_cmd;

	console_print("\n Available commands:\n");
	for(display_cmd = 0; display_cmd < cmd_no; display_cmd++)
	{
		console_print("%s  - %s\n", (char*)cmd_list[display_cmd].name,
								  (char*)cmd_list[display_cmd].description);
	}
}
//void set_tx1_attenuation(double* param, char param_no) // "tx1_attenuation=" command
//{
//	uint32_t attenuation_mdb;
//
//	if(param_no >= 1)
//	{
//		attenuation_mdb = param[0];
//		ad9361_set_tx_attenuation(ad9361_phy, 0, attenuation_mdb);
//		console_print("tx1_attenuation=%d\n", attenuation_mdb);
//	}
//	else
//		show_invalid_param_message(1);
//}
//void set_rx1_rf_gain(double* param, char param_no) // "rx1_rf_gain=" command
//{
//	int32_t gain_db;
//
//	if(param_no >= 1)
//	{
//		gain_db = param[0];
//		ad9361_set_rx_rf_gain (ad9361_phy, 0, gain_db);
//		console_print("rx1_rf_gain=%d\n", gain_db);
//	}
//	else
//		show_invalid_param_message(1);
//}
void set_rx1_ext_gain(double* param, char param_no) // "register?" command
{
	uint8_t data;
	struct spi_device spi;
	
	spi.id_no = 0;
	data = param[0];
	rx1_ext_gain(spi,data);
	if(data==0)
		console_print("set rx1 exit gain = 37.5dB\n");
	else
		console_print("set rx1 exit gain = 17.5dB\n");
	rx1_ext_gain_d = data;
}


//by z
void set_rx1_b_ext_gain(double* param, char param_no) // "register?" command
{
	uint8_t data;
	struct spi_device spi;
	
	spi.id_no = 1;  //by z
	data = param[0];
	rx1_ext_gain(spi,data);
	if(data==0)
		console_print("set rx1_b exit gain = 37.5dB\n");
	else
		console_print("set rx1_b exit gain = 17.5dB\n");
}


void set_rx1_ext_attn(double* param, char param_no) // "register?" command
{
	uint8_t data;
	struct spi_device spi;
	
	spi.id_no = 0;
	data = param[0];
	rx1_ext_attn(spi,data);
	console_print("set rx1 exit attn =%4.1f dB\n",data*0.5);
	rx1_ext_attn_d = data;
}


//by z
void set_rx1_b_ext_attn(double* param, char param_no) // "register?" command
{
	uint8_t data;
	struct spi_device spi;
	
	spi.id_no = 1;
	data = param[0];
	rx1_b_ext_attn(spi,data);
	console_print("set rx1_b exit attn =%4.1f dB\n",data*0.5);
}


void set_tx1_ext_gain(double* param, char param_no) // "register?" command
{
	uint8_t data;
	struct spi_device spi;

	spi.id_no = 0;
	data = param[0];
	tx1_ext_gain(spi,data);
	if(data==0)
		console_print("set tx1 exit gain = 37.5dB\n");
	else
		console_print("set tx1 exit gain = 17.5dB\n");
	tx1_ext_gain_d = data;
}

//by z
void set_tx1_b_ext_gain(double* param, char param_no) // "register?" command
{
	uint8_t data;
	struct spi_device spi;

	spi.id_no = 1;  //by z
	data = param[0];
	tx1_ext_gain(spi,data);
	if(data==0)
		console_print("set tx1 exit gain = 17.5dB\n");
	else
		console_print("set tx1 exit gain = 0dB\n");
}



void set_tx1_ext_attn(double* param, char param_no) // "register?" command
{
	uint8_t data;
	struct spi_device spi;

	spi.id_no = 0;
	data = param[0];
	tx1_ext_attn(spi,data);
	console_print("set tx1 exit attn =%4.1f dB\n",data*0.5);
	tx1_ext_attn_d = data;

}

void get_rx1_ext_gain(double* param, char param_no)
{
	console_print("get_rx1_ext_gain =%4.1f dB\n",rx1_ext_gain_d*0.5);
}
void get_rx1_ext_attn(double* param, char param_no)
{
	console_print("get_rx1_ext_attn =%4.1f dB\n",rx1_ext_attn_d*0.5);
}
void get_tx1_ext_gain(double* param, char param_no)
{
	console_print("get_tx1_ext_gain =%4.1f dB\n",tx1_ext_gain_d*0.5);
}
void get_tx1_ext_attn(double* param, char param_no)
{
	console_print("get_tx1_ext_attn =%4.1f dB\n",tx1_ext_attn_d*0.5);
}

//by z
//void set_tx1_b_ext_attn(double* param, char param_no) // "register?" command
//{
//	uint32_t data;
//	struct spi_device spi;
//
//	spi.id_no = 1;
//	data = param[0];
//	tx1_b_ext_attn(spi,data);
//	console_print("set tx1_b exit attn =%4.1f dB\n",data*0.5);
//
//}

//by fcm
void set_tx1_b_ext_attn(double* param, char param_no) // "register?" command
{
	static uint8_t flag = 0;
	double last_val = 0;
	double offset_ns;
	struct spi_device spi;
	uint32_t reset_val = 2147483647;	//0X7FFFFFFF
	const double step_one = 1.0/56;
	spi.id_no = 1;  //by z
	offset_ns = param[0];
	if(fabs(offset_ns-reset_val)<1e-6){
		tx1_b_ext_attn(spi,reset_val);
		console_print("reset OK\n");
		return;
	}
	while (fabs(last_val-offset_ns)>step_one)
	{
		uint32_t word = control_PPS(&last_val, &flag, offset_ns);
		tx1_b_ext_attn(spi,word);
		mdelay(1);
	}


	if(fabs(last_val-offset_ns)>1e-5)
		console_print("error: offset_ns=%16.3f last_val=%16.3f\n", offset_ns, last_val);
	else
		console_print("OK: last_val = %16.3f\n", last_val);

}

void bin_update_cmd(double* param, char param_no)
{
	BinUpdateMode = 1;
}
void get_FirmWare_Version(double* param, char param_no)
{
//	char *FPGA_VER = "20220429_LITE9361_40MSend_20MRecv\r\n";
//	char *ARM_VER =  "20220429_noos_BinUpdate\r\n";
	char *FPGA_VER = "2024112022single_AD9361_pps_led\r\n";
	char *ARM_VER =  "2024112022single_AD9361_pps_led\r\n";
	console_print("current fpga version:%s,current arm version:%s\r\n", FPGA_VER, ARM_VER);
	console_print("brief:add auto off/on signal\r\n");
}

#include "xadcps.h"
int XAdcFractionToInt(float FloatNum)
{
	float Temp;

	Temp = FloatNum;
	if (FloatNum < 0) {
		Temp = -(FloatNum);
	}

	return( ((int)((Temp -(float)((int)Temp)) * (1000.0f))));
}
u8 gXadcInitFinish = 0;
static XAdcPs XAdcInst;

void get_zynq_temp(double* param, char param_no)
{
	u32 TempRawData;
	XAdcPs_Config *ConfigPtr;
	XAdcPs *XAdcInstPtr = &XAdcInst;
	float TempData;
	if(gXadcInitFinish==0)
	{
		int Status;

		ConfigPtr = XAdcPs_LookupConfig(XPAR_XADCPS_0_DEVICE_ID);
		if (ConfigPtr == NULL) {
			console_print("Can't find XADC device.\r\n");
			return;
		}
		XAdcPs_CfgInitialize(XAdcInstPtr, ConfigPtr,ConfigPtr->BaseAddress);
		//XADC锟皆硷拷
		Status = XAdcPs_SelfTest(XAdcInstPtr);
		if (Status != XST_SUCCESS) {
			console_print("XADC selftest FAILED!\r\n");
			return;
		}

		TempRawData = XAdcPs_GetAdcData(XAdcInstPtr, XADCPS_CH_TEMP);
		TempData = XAdcPs_RawToTemperature(TempRawData);
		console_print("\r\nThe Current Temperature is %0d.%03d Centigrades.\r\n",(int)(TempData), XAdcFractionToInt(TempData));
		gXadcInitFinish = 1;
	}
	else
	{
		TempRawData = XAdcPs_GetAdcData(XAdcInstPtr, XADCPS_CH_TEMP);
		TempData = XAdcPs_RawToTemperature(TempRawData);
		console_print("\r\nThe Current Temperature is %0d.%03d Centigrades.\r\n",(int)(TempData), XAdcFractionToInt(TempData));
	}
}

void get_9361_temp(double* param, char param_no)
{
	int32_t temp;
	if(ad9361_get_temperature(ad9361_phy,&temp)==SUCCESS)
			console_print("\nphy_temperature : %4.2fC\r",(float)temp/1000);
}

extern void cmd_rom_dac(void);
void set_iic_device(double* param, char param_no)
{
	cmd_rom_dac();
}

